/**
 * System:      oshys
 * Model:       log
 * Licence:     GNU General Public License v3
 * Author:      sunwl
 * Date:        2010/02/19
 */
package org.oshys.util.logging;

// import java.text.DateFormat;
// import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;

/**
 * 异步Appender实现。<br>
 * AsyncAppender实现了IAppender接口，具有IAppender的所有特征。<br>
 * 实际上AsyncAppender是一个Appender的容器，它内置了一个包含Appender的列表，通过addAppender(IAppender)
 * 方法添加其它的Appender，<br>
 * 实际的Log输出还是掉用了之前添加到其中的Appender的append()方法。<br>
 * <b>示例</b><br>
 * AsyncAppender asyncAppender = new AsyncAppender();<br>
 * asyncAppender.addAppender(appenderA);<br>
 * asyncAppender.addAppender(appenderB);<br>
 * <br>
 * asyncAppender.append(logMessage);<br>
 * 等同于：<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;==>appenderA.append(logMessage);<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;==>appenderB.append(logMessage);<br>
 * 
 * @author sunwl
 */
public class AsyncAppender extends Appender {
    /**
     * 内置的Appender列表，保存各个实际调用的Appender。
     */
    private AppenderChain<IAppender> appenderList = new AppenderChain<IAppender>();

    private final Thread dispatcher;
    /**
     * 消息列表，保存待输出的消息。该列表中的消息会被其它线程消耗掉。
     */
    private List<LogMessage> msgQueue = new LinkedList<LogMessage>();;

    /**
     * 构造方法。<br>
     * 初始化异步Appender的分发线程，设置分发线程为后台。
     */
    public AsyncAppender() {
        dispatcher = new Thread(new Dispatcher(msgQueue, appenderList));
        dispatcher.setDaemon(true);
        dispatcher.setName("Dispatcher-" + dispatcher.getName());
        dispatcher.start();
    }

    /**
     * 异步输出一条Log。<br>
     * 该方法只是将消息加入消息列表后返回，分发消息的线程将自行把消息从消息列表中取出来并输出。
     */
    @Override
    public void append(LogMessage logMessage) {
        // 防止在分发线程异常终止后该异步Appender失效，但是此时已经无法做到异步。
        if (!dispatcher.isAlive()) {
            for (IAppender appender : appenderList) {
                appender.append(logMessage);
            }
            return;
        }
        // 加入消息列表
        synchronized (msgQueue) {
            msgQueue.add(logMessage);
            msgQueue.notifyAll();
        }
    }

    /**
     * 加入一个Appender。
     * 
     * @param appender
     */
    public void addAppender(IAppender appender) {
        this.appenderList.add(appender);
    }

    /**
     * 分发消息的线程。一般的该线程设置为后台模式，并一直运行，当该线程持有的消息队列中有消息时就调用各个Appender输出消息。
     * 
     * @author sunwl
     */
    private static class Dispatcher implements Runnable {

        private final AppenderChain<IAppender> appenderList;
        // 消息队列
        private final List<LogMessage> msgQueue;

        Dispatcher(final List<LogMessage> msgQueue, final AppenderChain<IAppender> appenderList) {
            this.msgQueue = msgQueue;
            this.appenderList = appenderList;
        }

        public void run() {
            try {
                while (true) {
                    synchronized (msgQueue) {
                        if (msgQueue == null || msgQueue.isEmpty()) {
                            msgQueue.wait();
                        }
                        if (msgQueue.size() > 0) {
                            msgQueue.notifyAll();

                            while (msgQueue.size() > 0) {
                                for (IAppender appender : appenderList) {
                                    appender.append(msgQueue.get(0));
                                    msgQueue.remove(0);
                                }
                            }
                        }
                    }
                }
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
//    public static void main(String args[]){
////        AsyncAppender appender = new AsyncAppender();
////        IAppender consoleAppender = new ConsoleAppender();
////        appender.addAppender(consoleAppender);
//        DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
//        for(int i = 0; i < 100;i++){
//            new AsyncLogImitate(String.format("%3d", i) + " " + dateformat.format(System.currentTimeMillis())
//                    ).start();
//        }
//    }
//}
//class AsyncLogImitate extends Thread{
//    String name = null;
//    AsyncLogImitate(String name){
//        this.name = name;
//    }
//    public void run(){
//        AsyncAppender appender = new AsyncAppender();
//        IAppender consoleAppender = new ConsoleAppender();
//        appender.addAppender(consoleAppender);
//        double ii = Math.random();
//        for(int i = 0; i < 100;i++){
//            LogMessage msg = new LogMessage(Level.DEBUG,name + ii, null);
//            appender.append(msg);
//        }
//    }
}
